package com.derbysoft.lowcostadapter.dswitch3.translate.impl;

import com.derbysoft.dswitch.dto.hotel.avail.*;
import com.derbysoft.dswitch.dto.hotel.common.*;
import com.derbysoft.dswitch.remote.hotel.dto.ErrorDTO;
import com.derbysoft.dswitch.remote.hotel.dto.HotelAvailRequest;
import com.derbysoft.dswitch.remote.hotel.dto.HotelAvailResponse;
import com.derbysoft.lowcostadapter.commons.dto.*;
import com.derbysoft.lowcostadapter.commons.utils.DateTimeUtils;
import com.derbysoft.lowcostadapter.dswitch2.service.helper.Constant;
import com.derbysoft.lowcostadapter.dswitch3.translate.Translator;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static com.derbysoft.lowcostadapter.dswitch3.translate.impl.DSwitch3Helper.groupByRoomType;

/**
 * Created by: jason
 * Date: 2012-06-18
 */
@Component("ds3HotelAvailRSTranslator")
public class DSwitch3AvailRSTranslator extends AbstractDSwitch3Translator
        implements Translator<SearchResponse, HotelAvailResponse, HotelAvailRequest> {
    private static final String NO_RESULT = "There are no results for your Search";
    public static final int DELAY_ONE_DAY = -24;

    @Override
    public HotelAvailResponse translate(SearchResponse searchResponse, HotelAvailRequest request) {
        HotelAvailResponse hotelAvailResponse = new HotelAvailResponse();
        hotelAvailResponse.setTaskId(request.getHeader().getTaskId());
        ReturnStatus returnStatus = searchResponse.getSearchResult().getReturnStatus();
        if (!returnStatus.isSuccess() || StringUtils.isNotBlank(returnStatus.getException())) {
            return failedAvailability(searchResponse, request, hotelAvailResponse);
        }
        hotelAvailResponse.setHotelAvailRS(createHotelAvailRS(searchResponse, request));
        if (CollectionUtils.isEmpty(hotelAvailResponse.getHotelAvailRS().getHotelAvailRoomStaysList())) {
            ArrayList<String> noAvailHotels = new ArrayList<String>();
            noAvailHotels.add(request.getHotelAvailRQ().getAvailCriteria().getHotelCodesList().get(0));
            hotelAvailResponse.getHotelAvailRS().setNoAvailHotelsList(noAvailHotels);
        }
        return hotelAvailResponse;
    }

    private HotelAvailResponse failedAvailability(SearchResponse searchResponse, HotelAvailRequest request, HotelAvailResponse hotelAvailResponse) {
        ArrayList<String> noAvailHotels = new ArrayList<String>();
        noAvailHotels.add(request.getHotelAvailRQ().getAvailCriteria().getHotelCodesList().get(0));
        HotelAvailRS hotelAvailRS = new HotelAvailRS();
        hotelAvailRS.setNoAvailHotelsList(noAvailHotels);
        hotelAvailResponse.setHotelAvailRS(hotelAvailRS);
        String errorMessage = searchResponse.getSearchResult().getReturnStatus().getException();
        if (StringUtils.contains(errorMessage, NO_RESULT)) {
            hotelAvailRS.setHotelAvailRoomStaysList(new ArrayList<HotelAvailRoomStayDTO>());
        } else {
            hotelAvailResponse.setError(createErrorDTO(searchResponse));
        }
        return hotelAvailResponse;
    }

    private HotelAvailRS createHotelAvailRS(SearchResponse searchResponse, HotelAvailRequest request) {
        HotelAvailRS hotelAvailRS = new HotelAvailRS();
        List<HotelAvailRoomStayDTO> hotelAvailRoomStayDTOs = new ArrayList<HotelAvailRoomStayDTO>();
        HotelAvailRoomStayDTO availRoomStayDTO = createAvailRoomStayDTO(searchResponse, request);
        if (CollectionUtils.isNotEmpty(availRoomStayDTO.getRoomStaysList())) {
            hotelAvailRoomStayDTOs.add(availRoomStayDTO);
        }
        hotelAvailRS.setHotelAvailRoomStaysList(hotelAvailRoomStayDTOs);
        return hotelAvailRS;
    }

    private ErrorDTO createErrorDTO(SearchResponse searchResponse) {
        ErrorDTO errorDTO = new ErrorDTO();
        errorDTO.setCode(Constant.SYSTEM_ERROR);
        errorDTO.setMessage(searchResponse.getSearchResult().getReturnStatus().getException());
        errorDTO.setSource(provider);
        return errorDTO;
    }

    private HotelAvailRoomStayDTO createAvailRoomStayDTO(SearchResponse searchResponse, HotelAvailRequest request) {
        HotelAvailRoomStayDTO hotelAvailRoomStayDTO = new HotelAvailRoomStayDTO();
        hotelAvailRoomStayDTO.setHotelCode(request.getHotelAvailRQ().getAvailCriteria().getHotelCodesList().get(0));
        hotelAvailRoomStayDTO.setRoomStaysList(createRoomStays(searchResponse, request));
        return hotelAvailRoomStayDTO;
    }

    private List<AvailRoomStayDTO> createRoomStays(SearchResponse searchResponse, HotelAvailRequest request) {
        ArrayList<AvailRoomStayDTO> availRoomStayDTOs = new ArrayList<AvailRoomStayDTO>();
        int numberOfUnit = request.getHotelAvailRQ().getAvailCriteria().getRoomStayCandidate().getNumberOfUnits();
        ArrayOfPropertyResult propertyResults = searchResponse.getSearchResult().getPropertyResults();
        if (CollectionUtils.isNotEmpty(propertyResults.getPropertyResult())) {
            PropertyResult propertyResult = propertyResults.getPropertyResult().get(0);
            Map<String, List<RoomType>> roomTypeGroup = groupByRoomType(propertyResult.getRoomTypes().getRoomType());
            for (List<RoomType> roomTypes : roomTypeGroup.values()) {
                if (roomTypes.size() >= numberOfUnit) {
                    availRoomStayDTOs.add(createAvailRoomStayDTO(roomTypes.get(0), request));
                }
            }
        }
        return availRoomStayDTOs;
    }

    private AvailRoomStayDTO createAvailRoomStayDTO(RoomType roomType, HotelAvailRequest request) {
        StayDateRangeDTO stayDateRange = request.getHotelAvailRQ().getAvailCriteria().getStayDateRange();
        AvailRoomStayDTO availRoomStayDTO = new AvailRoomStayDTO();
        availRoomStayDTO.setCurrency(DEFAULT_CURRENCY);
        availRoomStayDTO.setLanguage(request.getHotelAvailRQ().getAvailCriteria().getLanguage());
        availRoomStayDTO.setRatePlan(createRatePlan(roomType));
        availRoomStayDTO.setRoomType(createRoomType(roomType));
        availRoomStayDTO.setRoomRate(createRoomRate(roomType, stayDateRange));
        return availRoomStayDTO;
    }

    private RoomRateDTO createRoomRate(RoomType roomType, StayDateRangeDTO stayDateRange) {
        RoomRateDTO roomRateDTO = new RoomRateDTO();
        roomRateDTO.setRatesList(createRates(roomType, stayDateRange));
        return roomRateDTO;
    }

    private List<RateDTO> createRates(RoomType roomType, StayDateRangeDTO stayDateRange) {
        ArrayList<RateDTO> rateDTOs = new ArrayList<RateDTO>();
        RateDTO rateDTO = new RateDTO();
        BigDecimal lengthOfDate = BigDecimal.valueOf(DateTimeUtils.getIntervalDays(stayDateRange.getCheckin(), stayDateRange.getCheckout()));
        BigDecimal baseRate = roomType.getTotal().divide(lengthOfDate, 3, BigDecimal.ROUND_UP);
        rateDTO.setAmountBeforeTax(baseRate.doubleValue());
        rateDTO.setAmountAfterTax(baseRate.doubleValue());
        String end = DateTimeUtils.formatDate(DateTimeUtils.addHours(DateTimeUtils.parse(stayDateRange.getCheckout()), DELAY_ONE_DAY));
        rateDTO.setDateRange(new DateRangeDTO(stayDateRange.getCheckin(), end));
        rateDTOs.add(rateDTO);
        return rateDTOs;
    }

    private RoomTypeDTO createRoomType(RoomType roomType) {
        RoomTypeDTO roomTypeDTO = new RoomTypeDTO();
        roomTypeDTO.setCode(String.valueOf(roomType.getPropertyRoomTypeID()));
        roomTypeDTO.setName(roomType.getRoomType());
        roomTypeDTO.setDescription(roomType.getRoomType());
        return roomTypeDTO;
    }

    private RatePlanDTO createRatePlan(RoomType roomType) {
        RatePlanDTO ratePlanDTO = new RatePlanDTO();
        ratePlanDTO.setCode(String.valueOf(roomType.getMealBasisID()));
        ratePlanDTO.setName(roomType.getMealBasis());
        ratePlanDTO.setFreeMeal(createFreeMeal(roomType.getMealBasisID()));
        ratePlanDTO.setPaymentType(PaymentType.PREPAY);
        ratePlanDTO.setDescription(roomType.getMealBasis());
        return ratePlanDTO;
    }
}
